package com.sky.service.impl;


import cn.hutool.json.JSONUtil;
import com.github.pagehelper.PageHelper;

import com.sky.constant.MessageConstant;
import com.sky.context.BaseContext;
import com.sky.dto.OrdersPageQueryDTO;
import com.sky.dto.OrdersPaymentDTO;
import com.sky.dto.OrdersRejectionDTO;
import com.sky.dto.OrdersSubmitDTO;
import com.sky.entity.*;
import com.sky.exception.OrderBusinessException;
import com.sky.exception.ShoppingCartBusinessException;
import com.sky.mapper.*;
import com.sky.result.PageResult;
import com.sky.service.OrdersService;
import com.sky.utils.WeChatPayUtil;
import com.sky.vo.OrderPaymentVO;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.ws.WebSocketServer;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;


import com.github.pagehelper.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * PROJECT_NAME: sky-take-out
 * NAME: OrdersServiceImpl
 * USER: zhang
 * DATE: 2023/5/28
 * DESCRIPTION :C端-订单信息服务层接口实现类
 */
@Service
public class OrdersServiceImpl implements OrdersService {
    /**
     * 注入地址簿持久层接口实现类
     */
    @Resource
    private AddressBookMapper addressBookMapper;
    /**
     * 注入购物车持久层接口实现类
     */
    @Resource
    private ShoppingCartMapper shoppingCartMapper;
    /**
     * 注入订单信息持久层接口
     */
    @Resource
    private OrdersMapper ordersMapper;
    /**
     * 注入订单详情持久层接口实现类
     */
    @Resource
    private OrderDetailMapper orderDetailMapper;
    /**
     * 注入微信支付工具类
     */
    @Resource
    private WeChatPayUtil weChatPayUtil;
    /**
     * 注入用户信息持久层接口
     */
    @Resource
    private UserMapper userMapper;
    /**
     * 注入WebSocket的服务器端
     */
    @Resource
    private WebSocketServer webSocketServer;

    /**
     * 用户下单功能
     *
     * @param ordersSubmitDTO 订单基本信息
     * @return 全局通用返回信息Bean(订单id + 订单号 + 订单金额 + 下单时间)
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        /*
         * 1.完成订单信息表的数据填充
         * 2.完成订单详情信息表的数据填充
         * */
        //(1)判断用户地址Id是否合法(存在不存在)
        AddressBook addressBook = addressBookMapper.selectAddressBookById(ordersSubmitDTO.getAddressBookId());
        //不存在抛出异常
        if (Objects.isNull(addressBook)) {
            throw new OrderBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
        //(2)用户的购物车数据是否合法(存在不存在)
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCartCondition = ShoppingCart.builder().userId(userId).build();
        List<ShoppingCart> shoppingCartList = shoppingCartMapper.selectShoppingCart(shoppingCartCondition);
        if (Objects.isNull(shoppingCartList) || shoppingCartList.size() == 0) {
            throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        //(3)将OrdersSubmitDTO转换为Orders(订单对象)
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO, orders);
        //将时间毫秒值转换为字符串后作为订单编号
        orders.setNumber(String.valueOf(System.currentTimeMillis()));
        //设置订单的状态：待付款
        orders.setStatus(Orders.PENDING_PAYMENT);
        //指定下单用户Id
        orders.setUserId(userId);
        //指定下单时间为当前时间
        orders.setOrderTime(LocalDateTime.now());
        //指定支付方式：0未支付
        orders.setPayStatus(Orders.UN_PAID);
        //指定收货人名称
        orders.setConsignee(addressBook.getConsignee());
        //指定收货人手机号
        orders.setPhone(addressBook.getPhone());
        //指定收货人详细地址
        orders.setAddress(addressBook.FullAddress());
        //(4)将订单信息传递给持久层进行添加(主键返回)
        ordersMapper.submitOrders(orders);

        //(5)完成订单详情信息表的数据填充。将购物车中的数据转换为订单详情数据：ShoppingCart转换为OrderDetail对象
        List<OrderDetail> orderDetailList = shoppingCartList.stream().map((Function<ShoppingCart, OrderDetail>)
                shoppingCart -> {
                    OrderDetail orderDetail = new OrderDetail();
                    BeanUtils.copyProperties(shoppingCart, orderDetail);
                    orderDetail.setOrderId(orders.getId());
                    return orderDetail;
                }).collect(Collectors.toList());
        //将List<ShoppingCart>转换为List<OrderDetail>，再调用订单详情持久层完成订单详情数据的添加
        orderDetailMapper.batchInsertOrderDetail(orderDetailList);

        //End:创建OrderSubmitVO对象将订单编号/订单Id/订单总金额/下单时间，进行封装返回
        OrderSubmitVO orderSubmitVO = new OrderSubmitVO();
        //订单金额
        orderSubmitVO.setOrderAmount(orders.getAmount());
        //订单Id
        orderSubmitVO.setId(orders.getId());
        //订单编号
        orderSubmitVO.setOrderNumber(orders.getNumber());
        //下单时间
        orderSubmitVO.setOrderTime(orders.getOrderTime());
        return orderSubmitVO;
    }

    /**
     * 用户请求支付功能
     *
     * @param ordersPaymentDTO 用于请求支付相关信息(订单号码/付款方式)
     * @return 微信支付基础信息VO
     */
    @Override
    public OrderPaymentVO pay(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
        //获取本次请求支付订单的用户Id并查询用户信息
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.selectUserById(userId);
        //调用微信支付接口生成预支付交易单
        /*JSONObject payInfoJsonObject = weChatPayUtil.pay(
                ordersPaymentDTO.getOrderNumber(), //将订单编号传递给微信支付
                BigDecimal.valueOf(0.01),  //将订单金额传递给微信支付
                "苍穹外卖平台-订单",  //将收款标题传递给微信支付
                user.getOpenid()); //将付款人在微信的OPEN_ID传给微信支付
        if (Objects.nonNull(payInfoJsonObject.getString("code")) && "ORDERPAID".equals(payInfoJsonObject.getString("code"))) {
            throw new OrderBusinessException("订单已支付!");
        }
        OrderPaymentVO orderPaymentVO = payInfoJsonObject.toJavaObject(OrderPaymentVO.class);
        orderPaymentVO.setPackageStr(payInfoJsonObject.getString("package"));*/
        paySuccess(ordersPaymentDTO.getOrderNumber());
        return new OrderPaymentVO();
    }

    /**
     * 支付成功功能(更改订单状态)
     *
     * @param orderNumber 订单编号
     */
    @SneakyThrows //Lombok可以把必须要处理/声明的编译期异常基于该注解变成运行期
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void paySuccess(String orderNumber) {
        //根据当前用户Id与订单编号查询原始订单信息
        Orders orders = ordersMapper.selectOrderByOrderNumberAndUserId(BaseContext.getCurrentId(), orderNumber);
        //封装新的订单更新条件对象并进行更新
        Orders newOrder = Orders.builder().id(orders.getId()) //订单Id(基于订单Id更新)
                .status(Orders.TO_BE_CONFIRMED) //修改订单状态为待接单(Orders中的静态常量)
                .payStatus(Orders.PAID) //修改订单支付状态为已支付(Orders中的静态常量)
                .checkoutTime(LocalDateTime.now()) //结账时间
                .build();
        //支付成功后清空购物车
        shoppingCartMapper.clearShoppingCart(BaseContext.getCurrentId());
        ordersMapper.updateOrder(newOrder);
        //封装一个Map(发送给客户端的通知信息)
        HashMap<String, Object> infoMap = new HashMap<>();
        //弹框类型 1:待接单  2:催单
        infoMap.put("type", 1);
        //本次用户下单的订单Id
        infoMap.put("orderId", orders.getId());
        //通知信息：订单编号
        infoMap.put("content", "订单号：" + orderNumber);
        //当用户下单并且支付成功的时候，主动向所有在线的客户端，发出一个JSON格式的字符串
        String responseData = JSONUtil.toJsonStr(infoMap);
        webSocketServer.serverSendMessage2AllClient(responseData);
    }

    /**
     * 不同状态订单统计功能
     *
     * @return 全局通用返回信息
     */
    @Override
    public OrderStatisticsVO statisticsOrder() {
        //方案:使用持久层分组聚合查询不同状态的订单数伪表,并且只用一个集合返回List<Map<String,Object>>
        List<Map<String, Object>> resultList = ordersMapper.selectOrderCount();
        OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO();
        resultList.forEach(resultMap -> {

            switch ((Integer) resultMap.get("status")) {
                case 2:
                    //待接单
                    orderStatisticsVO.setToBeConfirmed(Integer.valueOf(resultMap.get("count").toString()));
                    break;
                case 3:
                    orderStatisticsVO.setConfirmed(Integer.valueOf(resultMap.get("count").toString()));
                    //待派送
                    break;
                case 4:
                    //派送中
                    orderStatisticsVO.setDeliveryInProgress(Integer.valueOf(resultMap.get("count").toString()));
                    break;
            }
        });
        return orderStatisticsVO;
    }

    /**
     * 订单搜索
     *
     * @param ordersPageQueryDTO 搜索条件参数
     * @return 全局通用返回信息Bean(总条数 + OrderVO)
     */
    @Override
    public PageResult selectOrderByCondition(OrdersPageQueryDTO ordersPageQueryDTO) {
        //开启分页
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        //基于条件查询订单信息
        Page<Orders> ordersPage = ordersMapper.selectOrderByCondition(ordersPageQueryDTO);
        //将Orders转换为OrderVO
        List<OrderVO> orderVOList = ordersPage.getResult().stream().map(new Function<Orders, OrderVO>() {
            @Override
            public OrderVO apply(Orders orders) {
                OrderVO orderVO = new OrderVO();
                //将orders拷贝到ordersVO
                BeanUtils.copyProperties(orders, orderVO);
                //基于订单详情持久层根据订单Id查询订单详情信息
                List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailByOrderId(orders.getId());
                orderVO.setOrderDetailList(orderDetailList);
                //将订单详情集合中的每一个订单详情中的每一个商品与件数拼接为一个字符串 例：鸡米花*3
                StringBuilder dishInfo = new StringBuilder();
                //遍历订单详情集合将订单详情中的每一个商品名称与件数拼接放到字符串容器中
                orderDetailList.forEach(orderDetail ->
                        dishInfo.append(orderDetail.getName()).append("*").append(orderDetail.getNumber()));
                //通过toString方法把字符串容器对象转换为字符串对象，封装到orderVO中
                String dish = dishInfo.toString();
                orderVO.setOrderDishes(dish);
                return orderVO;
            }
        }).collect(Collectors.toList());
        PageResult pageResult = PageResult.builder().total(ordersPage.getTotal()).records(orderVOList).build();
        return pageResult;
    }

    /**
     * 再来一单
     *
     * @param orderId 订单Id
     */
    @Override
    public void repetitionOrder(Long orderId) {
        List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailByOrderId(orderId);
        List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(orderDetail -> {
            ShoppingCart shoppingCart = new ShoppingCart();
            BeanUtils.copyProperties(orderDetail, shoppingCart, "id");
            shoppingCart.setCreateTime(LocalDateTime.now());
            shoppingCart.setUserId(BaseContext.getCurrentId());
            return shoppingCart;
        }).collect(Collectors.toList());
        shoppingCartList.forEach(shoppingCart -> shoppingCartMapper.insertShoppingCart(shoppingCart));
    }

    /**
     * 取消订单
     *
     * @param orderId 订单Id
     * @return 全局通用返回信息
     */
    @Override
    public void cancelOrder(Long orderId) {
        //查找本次要取消订单的基本信息
        Orders orders = ordersMapper.selectOrderById(orderId);
        //如果状态是待付款、待接单直接取消，如果不是直接抛出异常
        if (Objects.isNull(orders) || orders.getStatus() >= Orders.CONFIRMED) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //封装需要修改的订单Id、订单状态、支付状态、取消原因、取消时间
        Orders updateOrder = Orders.builder()
                .id(orderId)//要取消订单的Id
                .status(Orders.CANCELLED)//指定订单状态为取消
                .payStatus(Orders.REFUND)//指定支付状态为退款(模拟)
                .cancelReason("用户取消订单")//订单取消原因
                .cancelTime(LocalDateTime.now()).build();//取消时间
        //调用持久层完成更新
        ordersMapper.updateOrder(updateOrder);
    }

    /**
     * 查询订单详情
     *
     * @param orderId 订单Id
     * @return 全局通用返回信息Bean(订单基本信息 + 订单详细信息)
     */
    @Override
    public OrderVO selectOrderDetails(Long orderId) {
        //基于orderId查询订单基本信息
        Orders orders = ordersMapper.selectOrderById(orderId);
        //基于orderId查询订单详细信息
        List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailByOrderId(orderId);
        //封装OrderVO
        OrderVO orderVO = new OrderVO();
        BeanUtils.copyProperties(orders, orderVO);
        orderVO.setOrderDetailList(orderDetailList);
        return orderVO;
    }

    /**
     * 历史订单查询
     *
     * @param ordersPageQueryDTO 查询参数(页码+页数+状态)
     * @return 全局通用返回信息Bean(总条数 + OrderVO的List集合)
     */
    @Override
    public PageResult selectHistoryOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        //开启分页
        PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());
        //继续完成对DTO条件的封装，将当前用户的UserId作为参数封装
        ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());
        //调用订单持久层基于条件查询查询订单基本信息
        Page<Orders> ordersPage = ordersMapper.selectOrderByCondition(ordersPageQueryDTO);
        //将Orders转换为OrderVO
        List<OrderVO> orderVOList = ordersPage.getResult().stream().map(orders -> {
            OrderVO orderVO = new OrderVO();
            //将orders拷贝到orderVO中
            BeanUtils.copyProperties(orders, orderVO);
            //在转换过程中，通过订单详情持久层查询订单详情并封装到OrderVO中
            List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailByOrderId(orders.getId());
            orderVO.setOrderDetailList(orderDetailList);
            return orderVO;
        }).collect(Collectors.toList());
        //将OrderVOList集合放到PageResult中，将Page中的条数放到PageResult中
        PageResult pageResult = PageResult.builder().total(ordersPage.getTotal()).records(orderVOList).build();
        return pageResult;
    }

    /**
     * 查询订单详情
     *
     * @param orderId 订单Id
     * @return 全局通用返回信息Bean(订单信息 + 订单详情)
     */
    @Override
    public OrderVO selectOrderVO(Long orderId) {
        //基于订单Id查询订单基本信息
        Orders orders = ordersMapper.selectOrderById(orderId);
        OrderVO orderVO = new OrderVO();
        //将订单基本信息拷贝到OrderVO中
        BeanUtils.copyProperties(orders, orderVO);
        //查询订单详情
        List<OrderDetail> orderDetailList = orderDetailMapper.selectOrderDetailByOrderId(orderId);
        //封装为OrderVO
        orderVO.setOrderDetailList(orderDetailList);
        return orderVO;
    }

    /**
     * 接单
     *
     * @param orders 订单基本信息
     */
    @Override
    public void confirmOrder(Orders orders) {
        //查询当前要接单的基本信息
        Orders resultOrders = ordersMapper.selectOrderById(orders.getId());
        //如果当前要接单订单信息为空或者当前要接单的基本信息的订单状态不是待接单的话直接抛出异常(只有待接单才可以接单)
        if (Objects.isNull(resultOrders) || !resultOrders.getStatus().equals(Orders.TO_BE_CONFIRMED)) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //将订单状态设置为已接单
        orders.setStatus(Orders.CONFIRMED);
        //基于订单信息持久层修改订单状态
        ordersMapper.updateOrder(orders);
    }

    /**
     * 拒单
     *
     * @param ordersRejectionDTO 拒绝订单DTO(订单Id+拒单原因)
     */
    @Override
    public void rejectionOrder(OrdersRejectionDTO ordersRejectionDTO) {
        //查询当前要拒单的基本信息
        Orders resultOrders = ordersMapper.selectOrderById(ordersRejectionDTO.getId());
        //如果当前要接单订单信息为空或者当前要接单的基本信息的订单状态不是待接单的话直接抛出异常(只有待接单才可以拒单)
        if (Objects.isNull(resultOrders) || !resultOrders.getStatus().equals(Orders.TO_BE_CONFIRMED)) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersRejectionDTO, orders);
        //将订单状态设置为取消
        orders.setStatus(Orders.CANCELLED);
        //指定支付状态为退款
        orders.setPayStatus(Orders.REFUND);
        //指定取消原因为订单拒绝原因
        orders.setRejectionReason(orders.getRejectionReason());
        //指定取消时间为当前时间
        orders.setCancelTime(LocalDateTime.now());
        //基于订单信息持久层修改订单状态
        ordersMapper.updateOrder(orders);
    }

    /**
     * 商家取消订单
     *
     * @param orders 要取消的订单Id和订单取消原因
     */
    @Override
    public void adminCancelOrder(Orders orders) {
        //查询当前要取消订单的基本信息
        Orders resultOrders = ordersMapper.selectOrderById(orders.getId());
        //只有查询到的订单不为Null或者订单状态为待派送或者派送中才可以取消订单
        if (Objects.isNull(resultOrders)) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //如果要取消订单的状态小于3(待派送)或者大于4(派送中)直接抛出异常
        if (resultOrders.getStatus() < Orders.CONFIRMED || resultOrders.getStatus() > Orders.DELIVERY_IN_PROGRESS) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //将订单状态设置为取消
        orders.setStatus(Orders.CANCELLED);
        //指定支付状态为退款
        orders.setPayStatus(Orders.REFUND);
        //指定取消时间为当前时间
        orders.setCancelTime(LocalDateTime.now());
        //基于订单信息持久层修改订单状态
        ordersMapper.updateOrder(orders);
    }

    /**
     * 派送订单
     *
     * @param orderId 要派送的订单Id
     */
    @Override
    public void deliveryOrder(Long orderId) {
        //查询当前要派送订单的基本信息
        Orders resultOrders = ordersMapper.selectOrderById(orderId);
        //如果当前要派送的订单为Null或者订单状态不为待派送直接抛出异常
        if (Objects.isNull(resultOrders) || !resultOrders.getStatus().equals(Orders.CONFIRMED)) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //封装一个用于更新的orders对象指定订单状态为派送中
        Orders orders = Orders.builder().id(orderId).status(Orders.DELIVERY_IN_PROGRESS).build();
        //基于订单持久层修改订单状态
        ordersMapper.updateOrder(orders);
    }

    /**
     * 完成订单
     *
     * @param orderId 要完成订单Id
     */
    @Override
    public void completeOrder(Long orderId) {
        //查询当前要完成订单的基本信息
        Orders resultOrders = ordersMapper.selectOrderById(orderId);
        //如果当前要派送的订单为Null或者订单状态不为派送中直接抛出异常(只有派送中才可以完成订单)
        if (Objects.isNull(resultOrders) || !resultOrders.getStatus().equals(Orders.DELIVERY_IN_PROGRESS)) {
            throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);
        }
        //指定送达时间
        //指定订单状态为已完成
        Orders orders = Orders.builder().id(orderId).deliveryTime(LocalDateTime.now()).status(Orders.COMPLETED).build();
        //基于订单持久层修改订单状态
        ordersMapper.updateOrder(orders);
    }

    /**
     * 用户催单
     *
     * @param orderId 订单Id
     */
    @SneakyThrows
    @Override
    public void reminderOrder(Long orderId) {
        Orders orders = ordersMapper.selectOrderById(orderId);
        if (Objects.nonNull(orders) && orders.getStatus().equals(Orders.TO_BE_CONFIRMED) && orders.getPayStatus().equals(Orders.PAID)) {
            HashMap<String, Object> infoMap = new HashMap<>();
            infoMap.put("type", 2);
            infoMap.put("orderId", orderId);
            infoMap.put("content", "订单编号" + orders.getNumber());
            String responseData = JSONUtil.toJsonStr(infoMap);
            webSocketServer.serverSendMessage2AllClient(responseData);
        }
    }
}
